package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.StringUtils;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.common.core.utils.uuid.UUID;
import com.spzx.common.redis.annotation.MyRedisCache;
import com.spzx.product.api.domain.Product;
import com.spzx.product.api.domain.ProductDetails;
import com.spzx.product.api.domain.ProductSku;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.api.domain.vo.SkuPrice;
import com.spzx.product.api.domain.vo.SkuQuery;
import com.spzx.product.api.domain.vo.SkuStockVo;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.IProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品Service业务层处理
 */
@Slf4j
@Service
@Transactional
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {


    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;

    //@Autowired
    //private StringRedisTemplate stringRedisTemplate; //适合 key和value都是字符串类型

    @Autowired
    private RedisTemplate redisTemplate; //适合值是任意类型

    private static final String PRODUCT_SKU = "product:sku:";

    private static final String PRODUCT_ID = "product:id:";

    private static final String PRODUCT_SKUPRICEID = "product:skuPriceId:";

    private static final String PRODUCT_PRODUCTDETAILSID = "product:productDetailsID:";

    private static final String PRODUCT_SKUSPECVALUEID = "product:skuSpecValueID:";

    private static final String PRODUCT_SKUSTOCKVOID = "product:skuStockVoID:";

    private static final String SKU_CHECKANDLOCK = "sku:checkAndLock"; // 分布式锁，防止多次操作

    private static final String SKU_LOCK_DATA = "sku:lock:data"; // 不要重复提交，如果已经锁上了

    private static final String SKU_UNLOCK = "sku:unlock:";

    private static final String SKU_MINUS = "sku:minus:";
    /**
     * 查询商品列表
     *
     * @param product 商品
     * @return 商品
     */
    @Override
    public List<Product> selectProductList(Product product) {
        return productMapper.selectProductList(product);
    }

    //原子性
    @Override
    public int insertProduct(Product product) {
        //1.保存Product对象到product表
        productMapper.insert(product); //主键回填

        //2.保存List<ProductSku>对象到product_sku表
        List<ProductSku> productSkuList = product.getProductSkuList();
        if (CollectionUtils.isEmpty(productSkuList)) {
            throw new ServiceException("SKU数据为空");
        }
        int size = productSkuList.size();
        for (int i = 0; i < size; i++) {
            ProductSku productSku = productSkuList.get(i);
            productSku.setSkuCode(product.getId() + "_" + i);
            productSku.setSkuName(product.getName() + " " + productSku.getSkuSpec());
            productSku.setProductId(product.getId());
            productSkuMapper.insert(productSku);

            //添加商品库存  //3.保存List<SkuStock>对象到sku_stock表
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }

        //4.保存ProductDetails对象到product_details表
        ProductDetails productDetails = new ProductDetails();
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetails.setProductId(product.getId());
        productDetailsMapper.insert(productDetails);

        return 1;
    }


    @Override
    public Product selectProductById(Long id) {
        //1.根据id查询Product对象
        Product product = productMapper.selectById(id);

        //2.封装扩展字段：查询商品对应多个List<ProductSku>
        //select * from product_sku where product_id =?
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));
        List<Long> productSkuIdList = productSkuList.stream().map(productSku -> productSku.getId()).toList();


        // select * from sku_stock where sku_id in (1,2,3,4,5,6)
        List<SkuStock> skuStockList = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, productSkuIdList));

        Map<Long, Integer> skuIdToTatalNumMap = skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));
        productSkuList.forEach(productSku -> {
            //返回ProductSku对象，携带了库存数据；
            productSku.setStockNum(skuIdToTatalNumMap.get(productSku.getId()));
        });

        product.setProductSkuList(productSkuList);

        //3.封装扩展字段：商品详情图片List<String>
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
        String imageUrls = productDetails.getImageUrls();   //url,url,url
        String[] urls = imageUrls.split(",");
        product.setDetailsImageUrlList(Arrays.asList(urls));
        //返回Product对象
        return product;
    }


    @Override
    public int updateProduct(Product product) {
        //1 删除缓存
        List<Long> skuIdList =  product.getProductSkuList().stream()
                .map(ProductSku::getId).collect(Collectors.toList());
        skuIdList.forEach(skuId -> {
            String dataKey = PRODUCT_SKU + skuId;
            this.redisTemplate.delete(dataKey);
        });

        //2.更新Product
        productMapper.updateById(product);

        //3.更新SKU   List<ProductSku>
        List<ProductSku> productSkuList = product.getProductSkuList();
        if (CollectionUtils.isEmpty(productSkuList)) {
            throw new ServiceException("SKU数据为空");
        }
        productSkuList.forEach(productSku -> {
            productSkuMapper.updateById(productSku);

            //4.更新库存   List<ProductSku> -> 获取扩展字段stockNum
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setAvailableNum(skuStock.getTotalNum() - skuStock.getLockNum());
            skuStockMapper.updateById(skuStock);
        });

        //5.更新详情ProductDetails
        ProductDetails productDetails = productDetailsMapper
                .selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);

        //6 休眠一段时间
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //7 再次执行操作删除缓存
        skuIdList.forEach(skuId -> {
            String dataKey = "product:sku:" + skuId;
            this.redisTemplate.delete(dataKey);
        });
        return 1;
    }



    @Override
    public int deleteProductByIds(Long[] ids) {
        //1.删除Product表数据
        // delete from product where id in (1,2)
        productMapper.deleteBatchIds(Arrays.asList(ids));

        //2.删除ProductSku表数据
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, Arrays.asList(ids)));
        List<Long> productSkuIdList = productSkuList.stream().map(ProductSku::getId).toList();
        productSkuMapper.deleteBatchIds(productSkuIdList);

        //3.删除SkuStock表数据
        skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, productSkuIdList));

        //4.删除ProductDetails表数据
        // delete from product_details where product_id in (1,2)
        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>().in(ProductDetails::getProductId, Arrays.asList(ids)));
        return 1;
    }


    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        if (auditStatus == 1) {
            product.setAuditStatus(1);
            product.setAuditMessage("审批通过");
        } else {
            product.setAuditStatus(-1);
            product.setAuditMessage("审批拒绝");
        }
        productMapper.updateById(product);
    }

    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status == 1) {
            product.setStatus(1);

            String bitmapKey = "sku:product:data";

            List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId,id));
            productSkuList.forEach(item->{
                redisTemplate.opsForValue().setBit(bitmapKey,item.getId(),true);
            });
        } else {
            product.setStatus(-1);
        }
        productMapper.updateById(product);
    }


    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.getTopSale();
    }


    @Override
    public List<ProductSku> skuList(SkuQuery skuQuery) {
        return productSkuMapper.skuList(skuQuery);
    }


    /**
     * 服务提供者：6个接口来服务于商品详情查询。需要进行优化，提供查询效率。
     * 需要使用redis来提高性能。
     */

    @MyRedisCache(prefix = PRODUCT_SKU)
    @Override
    public ProductSku getProductSku(Long skuId) {

        return this.getProductSkuFromDB(skuId);
        /*try {
            ProductSku productSku = new ProductSku();
            // 查看redis有没有
            String productKey = "product:sku:" + skuId;

            if (redisTemplate.hasKey(productKey)) {
                productSku = (ProductSku) redisTemplate.opsForValue().get(productKey);
                return productSku;
            }

            // redis没有
            String lockKey = "product:sku:lock:" + skuId;
            String uuid = UUID.randomUUID().toString().replace("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    log.debug("product:sku:lock: 获取到锁");

                    productSku = this.getProductSkuFromDB(skuId);
                    long ttl = productSku == null ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(productKey, productSku, ttl, TimeUnit.SECONDS);
                    return productSku;
                } finally {
                    String scriptText = """
                            if redis.call(\"get\",KEYS[1]) == ARGV[1] 
                            then return redis.call(\"del\",KEYS[1])
                            else return 0
                            end
                            """;
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
                }

            } else {
                // 自旋
                try {
                    Thread.sleep(100);
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    log.info(e.toString());
                }
                return this.getProductSku(skuId);
            }
        } catch (Exception e) {
            log.info("保底措施" + e);
            return this.getProductSkuFromDB(skuId);
        }*/
    }


    public ProductSku getProductSkuFromDB(Long skuId) {
        ProductSku productSku = productSkuMapper.selectById(skuId);
        return productSku;
    }


    @MyRedisCache(prefix = PRODUCT_ID)
    @Override
    public Product getProduct(Long id) {
        return this.getProductFromDB(id);
        /*try {
            Product product = null;
            // 查看redis有没有
            String productKey = "product:id:" + id;

            if (redisTemplate.hasKey(productKey)) {
                product = (Product) redisTemplate.opsForValue().get(productKey);
                return product;
            }

            // redis没有
            String lockKey = "product:id:lock:" + id;
            String uuid = UUID.randomUUID().toString().replace("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    log.debug("product:id:lock: 获取到锁");

                    product = this.getProductFromDB(id);
                    long ttl = product == null ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(productKey, product, ttl, TimeUnit.SECONDS);
                    return product;
                } finally {
                    String scriptText = """
                            if redis.call(\"get\",KEYS[1]) == ARGV[1] 
                            then return redis.call(\"del\",KEYS[1])
                            else return 0
                            end
                            """;
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
                }

            } else {
                // 自旋
                try {
                    Thread.sleep(100);
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    log.info(e.toString());
                }
                return this.getProduct(id);
            }
        } catch (Exception e) {
            log.info("保底措施" + e);
            return this.getProductFromDB(id);
        }*/
    }


    public Product getProductFromDB(Long id){
        return productMapper.selectById(id);
    }

    @MyRedisCache(prefix = PRODUCT_SKUPRICEID)
    @Override
    public SkuPrice getSkuPrice(Long skuId) {

        return this.getSkuPriceFromDB(skuId);
        /*try {
            SkuPrice skuPrice = null;
            // 查看redis有没有
            String skuPriceKey = "product:skuPriceId:" + skuId;

            if (redisTemplate.hasKey(skuPriceKey)) {
                skuPrice = (SkuPrice) redisTemplate.opsForValue().get(skuPriceKey);
                return skuPrice;
            }

            // redis没有
            String lockKey = "product:skuPrice:lock:" + skuId;
            String uuid = UUID.randomUUID().toString().replace("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    log.debug("product:skuPrice:lock: 获取到锁");

                    skuPrice = this.getSkuPriceFromDB(skuId);
                    long ttl = skuPrice == null ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(skuPriceKey, skuPrice, ttl, TimeUnit.SECONDS);
                    return skuPrice;
                } finally {
                    String scriptText = """
                            if redis.call(\"get\",KEYS[1]) == ARGV[1] 
                            then return redis.call(\"del\",KEYS[1])
                            else return 0
                            end
                            """;
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
                }

            } else {
                // 自旋
                try {
                    Thread.sleep(100);
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    log.info(e.toString());
                }
                return this.getSkuPrice(skuId);
            }
        } catch (Exception e) {
            log.info("保底措施" + e);
            return this.getSkuPriceFromDB(skuId);
        }*/
    }

    public SkuPrice getSkuPriceFromDB(Long skuId){
        ProductSku productSku = productSkuMapper.selectOne(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getId, skuId).select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku, skuPrice);
        return skuPrice;
    }


    @MyRedisCache(prefix = PRODUCT_PRODUCTDETAILSID)
    @Override
    public ProductDetails getProductDetails(Long id) {
        return this.getProductDetailsFromDB(id);
        /*try {
            ProductDetails productDetails = null;
            // 查看redis有没有
            String productDetailsKey = "product:productDetailsID:" + id;

            if (redisTemplate.hasKey(productDetailsKey)) {
                productDetails = (ProductDetails) redisTemplate.opsForValue().get(productDetailsKey);
                return productDetails;
            }

            // redis没有
            String lockKey = "product:productDetails:lock:" + id;
            String uuid = UUID.randomUUID().toString().replace("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    log.debug("product:productDetails:lock: 获取到锁");

                    productDetails = this.getProductDetailsFromDB(id);
                    long ttl = productDetails == null ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(productDetailsKey, productDetails, ttl, TimeUnit.SECONDS);
                    return productDetails;
                } finally {
                    String scriptText = """
                            if redis.call(\"get\",KEYS[1]) == ARGV[1] 
                            then return redis.call(\"del\",KEYS[1])
                            else return 0
                            end
                            """;
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
                }

            } else {
                // 自旋
                try {
                    Thread.sleep(100);
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    log.info(e.toString());
                }
                return this.getProductDetails(id);
            }
        } catch (Exception e) {
            log.info("保底措施" + e);
            return this.getProductDetailsFromDB(id);
        }*/
    }

    public ProductDetails getProductDetailsFromDB(Long id) {
        return productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
    }


    @MyRedisCache(prefix = PRODUCT_SKUSPECVALUEID)
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        return this.getSkuSpecValueFromDB(id);
        /*try {
            Map<String, Long> SkuSpecValue = null;
            // 查看redis有没有
            String skuSpecValueKey = "product:skuSpecValueID:" + id;

            if (redisTemplate.hasKey(skuSpecValueKey)) {
                SkuSpecValue = (Map<String, Long>) redisTemplate.opsForValue().get(skuSpecValueKey);
                return SkuSpecValue;
            }

            // redis没有
            String lockKey = "product:skuSpecValue:lock:" + id;
            String uuid = UUID.randomUUID().toString().replace("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    log.debug("product:skuSpecValue:lock: 获取到锁");

                    SkuSpecValue = this.getSkuSpecValueFromDB(id);
                    long ttl = SkuSpecValue.size() == 0 ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(skuSpecValueKey, SkuSpecValue, ttl, TimeUnit.SECONDS);
                    return SkuSpecValue;
                } finally {
                    String scriptText = """
                            if redis.call(\"get\",KEYS[1]) == ARGV[1] 
                            then return redis.call(\"del\",KEYS[1])
                            else return 0
                            end
                            """;
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
                }

            } else {
                // 自旋
                try {
                    Thread.sleep(100);
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    log.info(e.toString());
                }
                return this.getSkuSpecValue(id);
            }
        } catch (Exception e) {
            log.info("保底措施" + e);
            return this.getSkuSpecValueFromDB(id);
        }*/
    }

    public Map<String, Long> getSkuSpecValueFromDB(Long id) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id).select(ProductSku::getId, ProductSku::getSkuSpec));
        Map<String, Long> skuSpecValueMap = new HashMap<>();
        productSkuList.forEach(item -> {
            skuSpecValueMap.put(item.getSkuSpec(), item.getId());
        });
        return skuSpecValueMap;
    }


    @MyRedisCache(prefix = PRODUCT_SKUSTOCKVOID)
    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        return this.getSkuStockFromDB(skuId);
        // 不使用切面时
        /*try {
            SkuStockVo skuStockVo = null;
            // 查看redis有没有
            String skuStockVoKey = "product:skuStockVoID:" + skuId;

            if (redisTemplate.hasKey(skuStockVoKey)) {
                skuStockVo = (SkuStockVo) redisTemplate.opsForValue().get(skuStockVoKey);
                return skuStockVo;
            }

            // redis没有
            String lockKey = "product:skuStockVo:lock:" + skuId;
            String uuid = UUID.randomUUID().toString().replace("-", "");
            Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 5, TimeUnit.SECONDS);
            if (ifAbsent) {
                try {
                    log.debug("product:skuStockVo:lock: 获取到锁");

                    skuStockVo = this.getSkuStockFromDB(skuId);
                    long ttl = skuStockVo == null ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(skuStockVoKey, skuStockVo, ttl, TimeUnit.SECONDS);
                    return skuStockVo;
                } finally {
                    String scriptText = """
                            if redis.call(\"get\",KEYS[1]) == ARGV[1] 
                            then return redis.call(\"del\",KEYS[1])
                            else return 0
                            end
                            """;
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), uuid);
                }

            } else {
                // 自旋
                try {
                    Thread.sleep(100);
                    log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());

                } catch (InterruptedException e) {
                    log.info(e.toString());
                }
                return this.getSkuStock(skuId);
            }
        } catch (Exception e) {
            log.info("保底措施" + e);
            return this.getSkuStockFromDB(skuId);
        }*/
    }

    public SkuStockVo getSkuStockFromDB(Long skuId) {
        SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }

    // select * from product_sku where id in (1,2,3)
    // select id,sale_price,market_price from product_sku where id in (1,2,3)
    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        if (CollectionUtils.isEmpty(skuIdList)) {
            return new ArrayList<SkuPrice>();
        }
        List<ProductSku> skuList = productSkuMapper
                .selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getId, skuIdList)
                        .select(ProductSku::getId, ProductSku::getSalePrice, ProductSku::getMarketPrice));
        if (CollectionUtils.isEmpty(skuList)) {
            return new ArrayList<SkuPrice>();
        }
        return skuList.stream().map((sku) -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(sku.getId());
            skuPrice.setSalePrice(sku.getSalePrice());
            skuPrice.setMarketPrice(sku.getMarketPrice());
            return skuPrice;
        }).toList();
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        // 确保不要重复提交
        String key = SKU_CHECKANDLOCK + orderNo;
        String datakey = SKU_LOCK_DATA + orderNo;

        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, 1, 1, TimeUnit.HOURS);

        if (!ifAbsent) {
            if (redisTemplate.hasKey(datakey)) {
                return "";
            }
            return "重复提交";
        }

        //确保检查库存和锁库存原子性，使用声明式事务
        skuLockVoList.forEach(skuLockVo -> {
            SkuStock hasStock = skuStockMapper.check(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (hasStock == null) {
                skuLockVo.setIsHaveStock(false);
            }else {
                skuLockVo.setIsHaveStock(true);
            }
        });

        // 解锁库存
        if (skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock())) {
            StringBuilder builerResult = new StringBuilder();
            skuLockVoList.stream().filter(skuLockVo -> !skuLockVo.getIsHaveStock()).
                    forEach(skuLockVo->builerResult.append(skuLockVo.getSkuId()+"库存不足"));
            redisTemplate.delete(key);
            return builerResult.toString();
        }

        // 锁库存
        skuLockVoList.forEach(skuLockVo -> {
            int rows = skuStockMapper.lock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (rows==0) {
                redisTemplate.delete(key);
                throw new ServiceException("锁库存失败");
            }
        });
        redisTemplate.opsForValue().set(datakey,skuLockVoList);
        return "";
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlock(String orderNo) {

        String lockKey = SKU_UNLOCK + orderNo;
        String dataKey = SKU_LOCK_DATA + orderNo;

        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 1, TimeUnit.HOURS);
        if (!ifAbsent) {
            return;
        }

        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);

        if (CollectionUtils.isEmpty(skuLockVoList)) {
            return;
        }

        // 解锁库存
        skuLockVoList.forEach(skuLockVo -> {
            int rows = skuStockMapper.unlock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (rows == 0){
                redisTemplate.delete(lockKey);
                throw new ServiceException("解锁库存失败");
            }
        });

        redisTemplate.delete(dataKey);
    }

    // 与释放库存类似
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void minus(String orderNo) {
        String lockKey = SKU_MINUS + orderNo;
        String dataKey = SKU_LOCK_DATA + orderNo;

        Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 1, TimeUnit.HOURS);
        if (!ifAbsent) {
            return;
        }

        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);

        if (CollectionUtils.isEmpty(skuLockVoList)) {
            return;
        }

        // 解锁库存
        skuLockVoList.forEach(skuLockVo -> {
            int rows = skuStockMapper.minus(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (rows == 0){
                redisTemplate.delete(lockKey);
                throw new ServiceException("减少库存失败");
            }
        });

        redisTemplate.delete(dataKey);

    }

}